<template>
  <div>
    <div style="color: #c1c1c1;">* 可拖动图片调整顺序</div>
    <div class="avatar-uploader-wrapper">
      <TransitionGroup tag="div" class="avatar-uploader-wrapper-imgList">
        <div
          v-for="(item,index) in imgList"
          :key="item.key"
          class="avatar-uploader"
          :style="imgStyle"
          draggable="true"
          @dragstart="handleDragStart($event, item)"
          @dragover.prevent="handleDragOver($event, item)"
          @dragenter="handleDragEnter($event, item)"
          @dragend="handleDragEnd($event, item)"
        >
          <img v-if="item.url" :src="item.url" class="avatar" alt="" />
          <i v-else class="avatar-uploader-icon" :style="imgStyle"><el-icon><Plus /></el-icon></i>
          <div v-if="item" class="opr-box" :style="imgStyle">
            <div class="icon-box">
              <div class="icon"><el-icon><EditPen /></el-icon></div>
              <input
                v-if="!disabled"
                :ref="el => editFileRefs[index] = el"
                title=""
                type="file"
                name="image"
                class="input"
                accept="image/*"
                @change="e => setImage(e,index)"
              />
            </div>
            <div class="icon-box" @click="deleteImg(index)">
              <div class="icon"><el-icon><Delete /></el-icon></div>
            </div>
          </div>
        </div>
      </TransitionGroup>
      <div key="add-btn" class="avatar-uploader" :class="disabled ? '' : 'avatar-uploader-hover'" :style="imgStyle">
        <i class="avatar-uploader-icon" :style="imgStyle"><el-icon><Plus /></el-icon></i>
        <input
          v-if="!disabled"
          ref="imgFileRef"
          type="file"
          name="image"
          class="upload-input"
          accept="image/*"
          @change="e => setImage(e,imgList.length)"
        />
      </div>

      <el-dialog
        v-model="dialogVisible"
        title="裁剪图片"
        width="30%"
      >
        <vue-cropper
          ref="cropperRef"
          :img="imgSrc"
          :auto-crop="autoCrop"
          :auto-crop-width="autoCropWidth||imgWidth"
          :auto-crop-height="autoCropHeight||imgHeight"
          :fixed-box="fixedBox"
          style="width:100%;height:300px;"
        />
        <template #footer>
          <el-button @click="cancelCrop">取 消</el-button>
          <el-button type="primary" @click="submitCrop">确 定</el-button>
        </template>
      </el-dialog>
    </div>
  </div>
</template>

<script setup>
import {Plus,Delete,EditPen} from '@element-plus/icons-vue'
import {VueCropper} from 'vue-cropper'
import 'vue-cropper/dist/index.css'
import oss from "@/plugins/ossUtil.js"
import { guid } from '@/utils'

const props = defineProps({
  list: {
    type: [Array, String],
    default: () => []
  },
  currentImg: {
    type: String,
    default: ''
  },
  autoCrop: {
    type: Boolean,
    default: true
  },
  imgWidth: {
    type: Number,
    default: 160
  },
  imgHeight: {
    type: Number,
    default: 160
  },
  uploadPath: {
    type: String,
    default: ''
  },
  limit: {
    type: Number,
    default: -1
  },
  disabled: {
    type: Boolean,
    default: false
  },
  autoCropWidth: {
    type: Number,
    default: undefined
  },
  autoCropHeight: {
    type: Number,
    default: undefined
  },
  fixedBox: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['update:list'])

// refs
const imgFileRef = ref(null)
const cropperRef = ref(null)
const editFileRefs = ref([])

// 响应式数据
const imgList = ref([])
const imgSrc = ref('')
const dialogVisible = ref(false)
const fileInfo = ref({
  name: '',
  type: ''
})
const idx = ref(0)
const dragging = ref(null)
const ending = ref(null)

// 计算属性
const imgStyle = computed(() => ({
  'width': props.imgWidth + 'px',
  'height': props.imgHeight + 'px',
  'line-height': props.imgHeight + 'px'
}))

// 监听属性变化
watch(() => props.list, (newVal) => {
  if (newVal) {
    imgList.value = typeof newVal === 'string'
        ? newVal.split(',').map(v => ({url: v, key: guid()}))
        : newVal.map(v => (typeof v === 'string' ? {url: v, key: guid()} : {
          ...v,
          key: guid()
        }))
  }
}, {immediate: true})

// 方法
const setImage = async (e, index) => {
  const file = e.target.files[0]
  if (!file.type.includes('image/')) {
    return
  }
  fileInfo.value = {
    name: file.name,
    type: file.type
  }

  if (!window.FileReader) return
  const reader = new FileReader()
  reader.onload = async (event) => {
    try {
      await $xl.modal.confirm('是否需要裁剪?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
      dialogVisible.value = true
      imgSrc.value = event.target.result
      idx.value = index
      cropperRef.value?.replace(event.target.result)
    } catch {
      setValue(event.target.result, index)
    }
    imgFileRef.value.value = ''
  }
  reader.readAsDataURL(file)
}

const setValue = (src, index) => {
  if (index >= imgList.value.length) {
    imgList.value.push({url: src, key: guid()})
  } else {
    imgList.value[index].url = src
  }
}

const deleteImg = async (index) => {
  try {
    await $xl.modal.confirm('是否要删除该图片吗？')
    imgList.value.splice(index, 1)
  } catch { /* empty */ }
}


const submitCrop = () => {
  let cropImg = null
  cropperRef.value.getCropData(data => {
    cropImg = data
    setValue(cropImg, idx.value)
  })
  dialogVisible.value = false
}

const cancelCrop = () => {
  dialogVisible.value = false
  setValue(imgSrc.value, idx.value)
}

const getImg = () => {
  return imgList.value.map(v => v.url)
}

const uploadImage = async () => {
  if (imgList.value.length < 1) {
    return null
  }
  for (let i = 0; i < imgList.value.length; i++) {
    if (imgList.value[i].url.indexOf('base64') > 0) {
      imgList.value[i].url = await oss.upload(imgList.value[i].url, props.uploadPath)
    }
  }
  return imgList.value.map(v => v.url).join(',')
}

// 拖拽相关方法
const handleDragStart = (event, item) => {
  dragging.value = item
  event.dataTransfer.setData('text/plain', '')
}

const handleDragEnd = (event, item) => {
  if (ending.value === dragging.value) return

  const src = imgList.value.indexOf(dragging.value)
  const dst = imgList.value.indexOf(ending.value)

  const temp = imgList.value[src]
  imgList.value[src] = imgList.value[dst]
  imgList.value[dst] = temp

  dragging.value = null
  ending.value = null
}

const handleDragOver = (e) => {
  e.dataTransfer.dropEffect = 'move'
}

const handleDragEnter = (e, item) => {
  e.dataTransfer.effectAllowed = 'move'
  ending.value = item
}

const clearAll = () => {
  imgList.value = []
}

// 暴露方法供父组件使用
defineExpose({
  getImg,
  uploadImage,
  clearAll
})
</script>

<style lang="scss" scoped>
.avatar-uploader-wrapper {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
}

.avatar-uploader {
  margin: 0 10px;
  display: inline-block;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  color: #97a8be;
  font-size: 14px;
  text-align: center;
  outline: none;
  background-color: #fff;
  transition: all linear 0.6s;

  &:hover .opr-box {
    opacity: 1;
    transition: opacity .3s;
  }

  .opr-box {
    opacity: 0;
    position: absolute;
    left: 0;
    top: 0;
    background-color: rgba(0, 0, 0, .3);
    z-index: 999;
    display: flex;

    .icon-box {
      flex: 1;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 24px;
      position: relative;
      cursor: move;

      .icon {
        cursor: pointer;
      }

      .input {
        height: 100%;
        width: 100%;
        left: 0;
        top: 0;
        position: absolute;
        opacity: 0;
      }
    }
  }

  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    text-align: center;
  }

  .avatar {
    width: 100%;
    height: 100%;
    display: inline-block;
  }

  .upload-input {
    width: 100%;
    height: 100%;
    z-index: 1;
    opacity: 0;
    position: absolute;
    left: 0;
    top: 0;
    cursor: pointer;
  }
}

.avatar-uploader-hover:hover {
  border-color: #409EFF;
}

:deep(.el-overlay-dialog){
  display: flex !important;
  justify-content: center !important;
}

</style>
